package com.shareyi.baseproject.kernel.adapter.spi.sequence;

import com.shareyi.baseproject.kernel.common.spi.sequence.SequenceProvider;

/**
 * 序列号提供服务默认实现
 *
 * @author david
 * @date 2021-06-02
 */
public class DefaultSequenceProvider implements SequenceProvider<Long> {

    /**
     * 集群唯一键保证器
     */
    private ClusterUniqueKeeper clusterUniqueKeeper;

    /**
     * 默认的重试次数，也可以外部传入一个
     * <p>
     * 设置足够大，失败的概率越低；但并发阻塞越大
     */
    private int defRetryTimes = 3;

    /**
     * 集群唯一性验证（默认开启，允许关闭）
     * <p>
     * 关闭情况下，分布式环境下可能出现多次ID重复的可能性；
     */
    private boolean clusterUniqueCheckEnable = true;

    /**
     * 序列号提供器
     *
     * @return
     */
    @Override
    public Long next(String type) {
        return getSnowflakeIdRetries(defRetryTimes, defRetryTimes);
    }


    /**
     * 在重试中尝试获取下一个主键
     *
     * @param leftRetryTimes
     * @param initRetryTimes
     * @return
     */
    private Long getSnowflakeIdRetries(int leftRetryTimes, int initRetryTimes) {
        if (leftRetryTimes == 0) {
            throw new RuntimeException("生成ID主键失败，尝试多次失败, n=" + initRetryTimes);
        }
        if (clusterUniqueCheckEnable && clusterUniqueKeeper == null) {
            throw new RuntimeException("生成ID主键失败，开启集群防重功能，但无集群防重保证服务");
        }
        Long nextId = ShortSnowIDGenerator.next();
        //如果集群uniqueKeeper为空， 跳过集群验证器
        boolean isShortTermUnique = !clusterUniqueCheckEnable || clusterUniqueKeeper.tryLock(nextId);
        if (isShortTermUnique) {
            return nextId;
        }
        return getSnowflakeIdRetries(leftRetryTimes - 1, initRetryTimes);
    }


    public int getDefRetryTimes() {
        return defRetryTimes;
    }

    public void setDefRetryTimes(int defRetryTimes) {
        this.defRetryTimes = defRetryTimes;
    }

    public void setClusterUniqueKeeper(ClusterUniqueKeeper clusterUniqueKeeper) {
        this.clusterUniqueKeeper = clusterUniqueKeeper;
    }

    public void setClusterUniqueCheckEnable(boolean clusterUniqueCheckEnable) {
        this.clusterUniqueCheckEnable = clusterUniqueCheckEnable;
    }
}
